﻿using LJ.DataAccess.Abstractions;
using Microsoft.EntityFrameworkCore;

namespace LJ.DataAccess.EntityFrameworkCore.Internal;

public abstract class EFTransactionalProviderBase(IDbContextFactory<DataAccessDbContext> contextFactory) : ITransactionalProvider
{
    public bool IsCommit { get; set; } = false;
    public bool IsRolledback { get; set; } = false;

    private readonly IDbContextFactory<DataAccessDbContext> _contextFactory = contextFactory;

    public DataAccessDbContext? Context;

    public async Task BeginAsync(CancellationToken cancellationToken = default)
    {
        var dbContext = GetDbContext();
        if (dbContext.Database.CurrentTransaction == null)
        {
            await dbContext.Database.BeginTransactionAsync(cancellationToken);
        }
    }

    public async Task CommitAsync(CancellationToken cancellationToken = default)
    {
        var dbContext = GetDbContext();
        if (dbContext.Database.CurrentTransaction != null)
        {
            if (IsCommit)
            {
                return;
            }

            if (IsRolledback)
            {
                return;
            }

            await SubmitAsync(cancellationToken);
            //var bulkConfig = new BulkConfig
            //{
            //    SetOutputIdentity = true,
            //    DoNotUpdateIfTimeStampChanged = true,
            //    SqlBulkCopyOptions = SqlBulkCopyOptions.CheckConstraints,
            //    UseTempDB = false
            //};
            //await dbContext.BulkSaveChangesAsync(bulkConfig, null, cancellationToken);
            //if (bulkConfig.TimeStampInfo != null && bulkConfig.TimeStampInfo.NumberOfSkippedForUpdate > 0)
            //{
            //    throw new DBConcurrencyException("Db Concurrency");
            //}

            await dbContext.Database.CommitTransactionAsync(cancellationToken);
            IsCommit = true;
        }
    }

    public async Task RollbackAsync(CancellationToken cancellationToken = default)
    {
        var dbContext = GetDbContext();
        if (dbContext.Database.CurrentTransaction != null)
        {
            if (IsCommit)
            {
                return;
            }

            if (IsRolledback)
            {
                return;
            }
            await dbContext.Database.RollbackTransactionAsync(cancellationToken);
            IsRolledback = true;
        }
    }
    public void Begin()
    {
        var dbContext = GetDbContext();
        if (dbContext.Database.CurrentTransaction == null)
        {
            dbContext.Database.BeginTransaction();
        }
    }

    public void Commit()
    {
        var dbContext = GetDbContext();
        if (dbContext.Database.CurrentTransaction != null)
        {
            if (IsCommit)
            {
                return;
            }

            if (IsRolledback)
            {
                return;
            }
            //var bulkConfig = new BulkConfig
            //{
            //    SetOutputIdentity = true,
            //    DoNotUpdateIfTimeStampChanged = true,
            //    SqlBulkCopyOptions = SqlBulkCopyOptions.CheckConstraints,
            //    UseTempDB = false
            //};
            // dbContext.BulkSaveChanges(bulkConfig, null);
            //if (bulkConfig.TimeStampInfo != null && bulkConfig.TimeStampInfo.NumberOfSkippedForUpdate > 0)
            //{
            //    throw new DBConcurrencyException("Db Concurrency");
            //}
            Submit();
            dbContext.Database.CommitTransaction();
            IsCommit = true;
        }
    }

    public void Rollback()
    {
        var dbContext = GetDbContext();
        if (dbContext.Database.CurrentTransaction != null)
        {
            if (IsCommit)
            {
                return;
            }

            if (IsRolledback)
            {
                return;
            }
            dbContext.Database.RollbackTransaction();
            IsRolledback = true;
        }
    }


    public DataAccessDbContext GetDbContext()
    {
        Context ??= _contextFactory.CreateDbContext();
        return Context;
    }

    public void Dispose()
    {
        Context?.Dispose();
        Context = null;
        GC.SuppressFinalize(this);
    }

    public abstract Task SubmitAsync(CancellationToken cancellationToken = default);

    public abstract void Submit();
}
